import { getId, Grid } from './MapInfo';

const { ccclass, property } = cc._decorator;

@ccclass
export default class PathFinding extends cc.Component {
	@property
	maxSearchCount = 5000;

	public search(startPoint: cc.Vec2, endPoint: cc.Vec2, blocks: any) {
		if (blocks[getId(startPoint.x, startPoint.y)]) {
			return null;
		}

		let path: cc.Vec2[] = [];
		let map = {};

		const startNode = new Grid(startPoint.x, startPoint.y);
		const endNode = new Grid(endPoint.x, endPoint.y);

		let openBase = Math.abs(startPoint.x - endPoint.x) + Math.abs(startPoint.y - endPoint.y);

		startNode.version = true;
		startNode.nowCost = 0;
		let openList: Grid[] = [startNode];

		let count = 0;
		let current: Grid;
		let neighbors: any[];

		while (true) {
			if (++count >= this.maxSearchCount) {
				break;
			}

			current = openList[0];
			openList[0] = current.next;
			if (openList[0]) {
				openList[0].pre = null;
			}

			if (current.x == endNode.x && current.y == endNode.y) {
				this.prunePath(startNode, current, blocks, path);
				break;
			}

			if (!current.neighbors) {
				current.checkNeighbors(map, blocks);
			}
			neighbors = current.neighbors;

			let test: Grid;
			let f: number;
			for (let i = 0; i < current.count; i++) {
				test = neighbors[i];
				f = current.nowCost + 1;
				if (!test.version) {
					test.version = true;
					test.parent = current;
					test.nowCost = f;
					test.dist = Math.abs(endPoint.x - test.x) + Math.abs(endPoint.y - test.y);

					f += test.dist;
					test.mayCost = f;

					f = (f - openBase) >> 1;

					test.pre = null;
					test.next = openList[f];

					if (openList[f]) {
						openList[f].pre = test;
					}

					openList[f] = test;
				} else {
					if (test.pre) {
						test.pre.next = test.next;
					}
					if (test.next) {
						test.next.pre = test.pre;
					}
					if (openList[1] == test) {
						openList[1] = test.next;
					}

					test.parent = current;
					test.nowCost = f;
					test.mayCost = f + test.dist;
					test.pre = null;
					test.next = openList[0];

					if (openList[0]) {
						openList[0].pre = test;
					}
					openList[0] = test;
				}
			}

			if (!openList[0]) {
				if (!openList[1]) {
					break;
				}

				let t = openList[0];
				openList[0] = openList[1];
				openList[1] = t;
				openBase += 2;
			}
		}

		return path;
	}

	private prunePath(startNode: Grid, endNode: Grid, blocks: any, path: cc.Vec2[]) {
		if (startNode.x == endNode.x && startNode.x == endNode.y) {
			return;
		}

		let parent: Grid;
		let current: Grid = endNode;
		let dx = current.x - endNode.x;
		let dy = current.y - endNode.y;

		while (true) {
			//回到起点，路径完成
			if (current.x == startNode.x && current.y == startNode.y) {
				path.push(cc.v2(current.x, current.y));
				return;
			}

			parent = current.parent;

			if (parent != startNode) {
				let ppx = parent.parent.x,
					ppy = parent.parent.y;

				if (Math.abs(ppx - current.x) == 1 && Math.abs(ppy - current.y) == 1 && !blocks[getId(current.y, ppx)] && !blocks[getId(ppy, current.x)]) {
					parent = parent.parent;
				}
			}

			if (parent.x - current.x == dx && parent.y - current.y == dy) {
				current = parent;
			} else {
				dx = parent.x - current.x;
				dy = parent.y - current.y;
				path.push(cc.v2(current.x, current.y));
				current = parent;
			}
		}
	}
}
